//
//  DXMObject.h
//  DerbyStatter
//
//  Created by glenn andreas on 1/6/14.
//  Copyright (c) 2014 glenn andreas. All rights reserved.
//

#import <Foundation/Foundation.h>

/*
 DXMLObject is a basic "xml data" object, not unlike NSXMLNode/etc... (which don't exist on iOS anyway)
 */

@class DXMLTimestamp;
@interface DXMLObject : NSObject
- (id) initWithElementName: (NSString *) elementName attributes: (NSDictionary *) attributes;
@property (weak) DXMLObject *parent;
@property (readonly) NSString *elementName;
+ (NSString *) elementName;

// Despite being readonly and/or immutable, these are all mutable
@property (readonly) NSArray *children;
@property (readonly) NSDictionary *attributes;
// passing nil for attribute will remove it
- (void) setAttribute: (NSString *) attribute forKey: (NSString *) key;
- (NSString *) attributeForKey: (NSString *) key;

- (void) setIntAttribute: (NSInteger) attribute forKey: (NSString *) key;
- (NSInteger) intAttributeForKey: (NSString *) key;
- (NSInteger) intAttributeForKey: (NSString *) key defaultValue: (NSInteger) defaultValue;

- (void) setBoolAttribute: (BOOL) attribute forKey: (NSString *) key;
- (BOOL) boolAttributeForKey: (NSString *) key;
- (BOOL) boolAttributeForKey: (NSString *) key defaultValue: (BOOL) defaultValue;

@property (strong) NSString *text;
- (void) addChild: (DXMLObject *) child;
- (void) appendText: (NSString *) text;
- (NSString *) xmlString;

- (void) removeChild: (DXMLObject *) child;

// manipulation of children via element name
- (id) firstChildWithElementName: (NSString *) name;
- (void) setFirstChild: (DXMLObject *)child withElementName: (NSString *) name;

- (NSArray *) allChildrenWithElementName: (NSString *) name;
- (void) setChildren: (NSArray *)children withElementName: (NSString *) name;
// subset of XPath.  This has the same support
// as python's xml.etree.elementtree
// http://docs.python.org/2/library/xml.etree.elementtree.html#supported-xpath-syntax
//  tag	Selects all child elements with the given tag. For example, spam selects all child elements named spam, spam/egg selects all grandchildren named egg in all children named spam.
//  *	Selects all child elements. For example, */egg selects all grandchildren named egg.
//  .	Selects the current node. This is mostly useful at the beginning of the path, to indicate that it’s a relative path.
//  //	Selects all subelements, on all levels beneath the current element. For example, .//egg selects all egg elements in the entire tree.
//  ..	Selects the parent element.
//  [@attrib]	Selects all elements that have the given attribute.
//  [@attrib='value']	Selects all elements for which the given attribute has the given value. The value cannot contain quotes.
//  [tag]	Selects all elements that have a child named tag. Only immediate children are supported.
//  [position]	Selects all elements that are located at the given position. The position can be either an integer (1 is the first position), the expression last() (for the last position), or a position relative to the last position (e.g. last()-1).

- (DXMLObject *) find: (NSString *) path;
- (NSArray *) findAll: (NSString *) path;

// since a vast number of objects have time stamps, easier to put it here in the root
@property (strong) DXMLTimestamp *timestamp;
@property (readonly) DXMLTimestamp *closestTimestamp; // either our timstamp, or our parents closestTimestamp
// Some common utils.
@property (strong) NSString *name;
- (float) nameIsSimilar: (NSString *) name;
- (NSString *) filterName: (NSString *) name withOnlyCharactersInSet: (NSCharacterSet *) set;
- (id) enclosingParentOfClass: (Class) cls;
@end
// We encapsulte "notes" from our children into an ivar
@interface DXMLObject(Notes)
@property (strong) NSString *notes;
@end

// Macros to make it easier to have properties backed by attributes
#define DXMLSTRINGATTR(name,Name) \
- (NSString *) name { return [self attributeForKey: @ #name]; } \
- (void) set ## Name: (NSString *) value { [self setAttribute: value forKey: @ #name]; }
#define DXMLSTRINGATTRKEY(name,Name,key) \
- (NSString *) name { return [self attributeForKey: key]; } \
- (void) set ## Name: (NSString *) value { [self setAttribute: value forKey: key]; }

#define DXMLINTATTR(name,Name) \
- (NSInteger) name { return [self intAttributeForKey: @ #name] ; } \
- (void) set ## Name: (NSInteger) value { [self setIntAttribute:value forKey: @ #name]; }
#define DXMLINTATTRKEY(name,Name,key) \
- (NSInteger) name { return [self intAttributeForKey: key] ; } \
- (void) set ## Name: (NSInteger) value { [self setIntAttribute:value forKey: key]; }
#define DXMLINTATTRDEFAULT(name,Name,dfltValue) \
- (NSInteger) name { return [self intAttributeForKey: @ #name defaultValue: dfltValue] ; } \
- (void) set ## Name: (NSInteger) value { [self setIntAttribute:value forKey: @ #name]; }
#define DXMLINTATTRKEYDEFAULT(name,Name,key,dfltValue) \
- (NSInteger) name { return [self intAttributeForKey: key  defaultValue: dfltValue] ; } \
- (void) set ## Name: (NSInteger) value { [self setIntAttribute:value forKey: key]; }

#define DXMLBOOLATTR(name,Name) \
- (BOOL) name { return [self boolAttributeForKey: @ #name]; } \
- (void) set ## Name: (BOOL) value { [self setBoolAttribute: value forKey: @ #name]; }
#define DXMLBOOLATTRKEY(name,Name,key) \
- (BOOL) name { return [self boolAttributeForKey: key]; } \
- (void) set ## Name: (BOOL) value { [self setBoolAttribute: value forKey: key]; }
#define DXMLBOOLATTRDEFAULT(name,Name,dfltValue) \
- (BOOL) name { return [self boolAttributeForKey: @ #name defaultValue: dfltValue]; } \
- (void) set ## Name: (BOOL) value { [self setBoolAttribute: value forKey: @ #name]; }
#define DXMLBOOLATTRKEYDEFAULT(name,Name,key,dfltValue) \
- (BOOL) name { return [self boolAttributeForKey: key]; } \
- (void) set ## Name: (BOOL) value { [self setBoolAttribute: value forKey: key defaultValue: dfltValue]; }


#define DXMLCHILDATTR(name,Name, cls) \
- (cls *) name { return [self firstChildWithElementName: [cls elementName]]; } \
- (void) set ## Name: (cls *) value { [self setFirstChild: value withElementName: [cls elementName]]; }

#define DXMLCHILDRENATTR(name,Name, cls) \
- (NSArray *) name { return [self allChildrenWithElementName: [cls elementName]]; } \
- (void) set ## Name: (NSArray *) value { [self setChildren: value withElementName: [cls elementName]]; }
